package br.com.educa.modelo.academico;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.PersistenceContext;
import javax.persistence.Table;
import javax.persistence.TypedQuery;
import javax.persistence.Version;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.roo.addon.json.RooJson;
import org.springframework.roo.addon.serializable.RooSerializable;
import org.springframework.roo.addon.tostring.RooToString;
import org.springframework.transaction.annotation.Transactional;

import br.com.educa.modelo.disciplinar.Elogios;
import br.com.educa.modelo.disciplinar.Meritos;
import br.com.educa.modelo.disciplinar.Punicoes;
import br.com.educa.modelo.financeiro.LancamentosAlunos;
import br.com.educa.modelo.pessoa.Pessoas;
import br.com.educa.modelo.pessoa.enumerator.Cor;
import br.com.educa.modelo.pessoa.enumerator.Religiao;
import flexjson.JSONDeserializer;
import flexjson.JSONSerializer;

@Configurable
@Entity
@Table
@RooJavaBean
@RooSerializable
@RooToString
@RooJson
@RooJpaActiveRecord(finders = { "findAlunosesByPessoaAluno" })
public class Alunos implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Version
    @Column(name = "version")
    private Integer version;

    @ManyToOne
    @JoinColumn(name = "pessoaAluno", referencedColumnName = "id", nullable = false)
    private Pessoas pessoaAluno;

    private String matricula;

    private String ativo;

    private String escolaOrigem;

    @Enumerated(EnumType.STRING)
    private Cor cor;

    private String filhoDe;

    private String pai;

    private String mae;
    
    @OneToMany(mappedBy = "aluno")
    private Set<LancamentosAlunos> lancamentos;
    
    //incluir o tipo do responsável
    
    @Pattern(regexp = "^([0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\\w]*[0-9a-zA-Z]\\.)+[a-zA-Z]{2,9})$", message = "Email inválido!")
    private String emailResponsavel;

    @Enumerated(EnumType.STRING)
    private Religiao religiao;

    @NotNull
    private Double comportamento;
    
    @OneToMany(mappedBy = "aluno")
    private Set<Punicoes> punicoes;

    @OneToMany(mappedBy = "aluno")
    private Set<Elogios> elogios;
    
    @OneToMany(mappedBy = "aluno")
    private Set<Meritos> meritos;
    
    @PersistenceContext
    transient EntityManager entityManager;

    private static final long serialVersionUID = 1L;

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Integer getVersion() {
        return this.version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public Pessoas getPessoaAluno() {
        return this.pessoaAluno;
    }

    public void setPessoaAluno(Pessoas pessoaAluno) {
        this.pessoaAluno = pessoaAluno;
    }

    public String getMatricula() {
        return this.matricula;
    }

    public void setMatricula(String matricula) {
        this.matricula = matricula;
    }

    public String getAtivo() {
        return this.ativo;
    }

    public void setAtivo(String ativo) {
        this.ativo = ativo;
    }

    public String getEscolaOrigem() {
        return this.escolaOrigem;
    }

    public void setEscolaOrigem(String escolaOrigem) {
        this.escolaOrigem = escolaOrigem;
    }

    public Cor getCor() {
        return this.cor;
    }

    public void setCor(Cor cor) {
        this.cor = cor;
    }

    public String getFilhoDe() {
        return this.filhoDe;
    }

    public void setFilhoDe(String filhoDe) {
        this.filhoDe = filhoDe;
    }

    public String getPai() {
        return this.pai;
    }

    public void setPai(String pai) {
        this.pai = pai;
    }

    public String getMae() {
        return this.mae;
    }

    public void setMae(String mae) {
        this.mae = mae;
    }

    public Religiao getReligiao() {
        return this.religiao;
    }

    public void setReligiao(Religiao religiao) {
        this.religiao = religiao;
    }

    public Double getComportamento() {
        return this.comportamento;
    }

    public void setComportamento(Double comportamento) {
        this.comportamento = comportamento;
    }

    public static final EntityManager entityManager() {
        EntityManager em = new Alunos().entityManager;
        if (em == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
        return em;
    }

    public static long countAlunoses() {
        return entityManager().createQuery("SELECT COUNT(o) FROM Alunos o", Long.class).getSingleResult();
    }

    public static List<br.com.educa.modelo.academico.Alunos> findAllAlunoses() {
        return entityManager().createQuery("SELECT o FROM Alunos o", Alunos.class).getResultList();
    }

    public static br.com.educa.modelo.academico.Alunos findAlunos(Long id) {
        if (id == null) return null;
        return entityManager().find(Alunos.class, id);
    }

    public static List<br.com.educa.modelo.academico.Alunos> findAlunosEntries(int firstResult, int maxResults) {
        return entityManager().createQuery("SELECT o FROM Alunos o", Alunos.class).setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
    }

    @Transactional
    public void persist() {
        if (this.entityManager == null) this.entityManager = entityManager();
        this.entityManager.persist(this);
    }

    @Transactional
    public void remove() {
        if (this.entityManager == null) this.entityManager = entityManager();
        if (this.entityManager.contains(this)) {
            this.entityManager.remove(this);
        } else {
            Alunos attached = Alunos.findAlunos(this.id);
            this.entityManager.remove(attached);
        }
    }

    @Transactional
    public void flush() {
        if (this.entityManager == null) this.entityManager = entityManager();
        this.entityManager.flush();
    }

    @Transactional
    public void clear() {
        if (this.entityManager == null) this.entityManager = entityManager();
        this.entityManager.clear();
    }

    @Transactional
    public br.com.educa.modelo.academico.Alunos merge() {
        if (this.entityManager == null) this.entityManager = entityManager();
        Alunos merged = this.entityManager.merge(this);
        this.entityManager.flush();
        return merged;
    }

    public String toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }

    public String toJson() {
        return new JSONSerializer().exclude("*.class").serialize(this);
    }

    public static br.com.educa.modelo.academico.Alunos fromJsonToAlunos(String json) {
        return new JSONDeserializer<Alunos>().use(null, Alunos.class).deserialize(json);
    }

    public static String toJsonArray(Collection<br.com.educa.modelo.academico.Alunos> collection) {
        return new JSONSerializer().exclude("*.class").serialize(collection);
    }

    public static Collection<br.com.educa.modelo.academico.Alunos> fromJsonArrayToAlunoses(String json) {
        return new JSONDeserializer<List<Alunos>>().use(null, ArrayList.class).use("values", Alunos.class).deserialize(json);
    }

    public static TypedQuery<br.com.educa.modelo.academico.Alunos> findAlunosesByPessoaAluno(Pessoas pessoaAluno) {
        if (pessoaAluno == null) throw new IllegalArgumentException("The pessoaAluno argument is required");
        EntityManager em = Alunos.entityManager();
        TypedQuery<Alunos> q = em.createQuery("SELECT o FROM Alunos AS o WHERE o.pessoaAluno = :pessoaAluno order by o.id DESC", Alunos.class);
        q.setParameter("pessoaAluno", pessoaAluno);
        return q;
    }
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static List<HashMap<String, Object>> agruparPorElogioPunicaoMerito(Long aluno, Integer ano) {
    	List<HashMap<String, Object>> comportamentos = new ArrayList<HashMap<String, Object>>();
    	List<Object[]> resultSet = new ArrayList<Object[]>();
//    	Calendar c1 = Calendar.getInstance();
//    	c1.set(ano.intValue(), Calendar.JANUARY, 1);  //January 30th 2000
//    	Date iDate = c1.getTime();
//    	c1.set(ano.intValue(), Calendar.DECEMBER, 31);  //January 30th 2000
//    	Date fDate = c1.getTime(); 
    	 
    	try{
//	    	javax.persistence.Query q = entityManager().createNativeQuery("SELECT distinct(id), tipo, ocorrencia, valor, data_registro, descricao" +
//														" FROM (select a.id, 'PUNIÇÃO' as tipo, c.punicao as ocorrencia, c.valor, a.data_registro, a.descricao" +
//														"    from punicoes a, alunos b, punicoes_tipos c" +
//														"    where c.id = a.punicao_tipo" +
//														"      and b.id = a.aluno" +
//														" UNION ALL" +
//														" select a.id, 'ELOGIO' as tipo, c.elogio as ocorrencia, c.valor,  a.data_registro, a.descricao" +
//														"    from elogios a, alunos b, elogios_tipos c" +
//														"    where c.id = a.elogio_tipo" +
//														"      and b.id = a.aluno" +
//														" UNION ALL " +
//														" select a.id, 'MÉRITO' as tipo, c.merito as ocorrencia, 0 as valor,  a.data_registro, a.descricao" +
//														"    from meritos a, alunos b, meritos_tipos c" +
//														"    where c.id = a.merito_tipo" +                             
//														"      and b.id = a.aluno) abacaxi" +
//														" WHERE " +
//														" id = ?" +
//														" AND " +
//														" data_registro BETWEEN ? AND ?" +
//														" ORDER BY" +
//														" data_registro");
    		/*
    		 * Definições dos tipos de diciplinar:
    		 * Elogio é definido por +;
    		 * Punição é definido por -;
    		 * Mérito não interfere na contagem de comportamento.
    		 */
        	javax.persistence.Query q = entityManager().createNativeQuery("SELECT id, aluno, tipo, ocorrencia, valor, data_registro, descricao " +
					"FROM (select a.id, a.aluno, 'PUNIÇÃO' as tipo, c.punicao as ocorrencia, c.valor, a.data_registro, a.descricao " +
					"   from punicoes a, alunos b, punicoes_tipos c " +
					"   where c.id = a.punicao_tipo " +
					"     and b.id = a.aluno " +
					"UNION ALL " +
					"select a.id, a.aluno, 'ELOGIO' as tipo, c.elogio as ocorrencia, c.valor,  a.data_registro, a.descricao " +
					"   from elogios a, alunos b, elogios_tipos c " +
					"   where c.id = a.elogio_tipo " +
					"     and b.id = a.aluno " +
					"UNION ALL  " +
					"select a.id, a.aluno, 'MÉRITO' as tipo, c.merito as ocorrencia, 0 as valor,  a.data_registro, a.descricao " +
					"   from meritos a, alunos b, meritos_tipos c " +
					"   where c.id = a.merito_tipo " +                             
					"     and b.id = a.aluno) abacaxi " +
					"WHERE  " +
					"aluno = ? " +
					"ORDER BY  " +
					"data_registro, id");

	    	((javax.persistence.Query) q).setParameter(1, aluno);
	//    	((javax.persistence.Query) q).setParameter(2, iDate);
	//    	((javax.persistence.Query) q).setParameter(3, fDate);
	    	resultSet = ((javax.persistence.Query) q).getResultList();

	    	for (Object[] row : resultSet) {
				HashMap<String,Object> objetoResultado = new HashMap<String, Object>();
				objetoResultado.put("id",((BigInteger)row[0]).intValue());
				objetoResultado.put("aluno",((BigInteger)row[1]).intValue());
				objetoResultado.put("tipo",row[2].toString());
				objetoResultado.put("ocorrencia",row[3].toString());
				objetoResultado.put("valor",row[4].toString());
				objetoResultado.put("data_registro",(Date)row[5]);
				objetoResultado.put("descricao",row[6].toString());
				comportamentos.add(objetoResultado);
			}
	    	//calcular o comportamento.
	    	Alunos al = Alunos.findAlunos(aluno);
	        BigDecimal comportamento = new BigDecimal(0.00d);
	        for (HashMap<String, Object> objeto : comportamentos) {
	        	String valorStr = objeto.get("valor").toString();
	        	BigDecimal valor = new BigDecimal(Double.parseDouble(valorStr));
	        	if(objeto.get("tipo").toString().equals("ELOGIO")){
	        		comportamento = comportamento.add(valor).setScale(2,BigDecimal.ROUND_HALF_DOWN);
	        		if(comportamento.compareTo(BigDecimal.TEN) == 1){comportamento= BigDecimal.TEN;}
	        	}else if(objeto.get("tipo").toString().equals("PUNIÇÃO")){
	        		comportamento = comportamento.subtract(valor).setScale(2,BigDecimal.ROUND_HALF_DOWN);
	        		if(comportamento.compareTo(BigDecimal.ZERO) == -1){comportamento= BigDecimal.ZERO;}
	        	}
	        	//tentar forçar na string o decimal.
//	        	comportamento = new BigDecimal());	        	
	        }
	        //comportamento = comportamento.setScale(2);
	        al.setComportamento(Double.parseDouble(comportamento.toString()));
	        al.merge();	    	
    	
    	} catch (Exception e) {
            e.printStackTrace();
        }
    	return comportamentos;
    }

	public Set<Punicoes> getPunicoes() {
        return this.punicoes;
    }

	public void setPunicoes(Set<Punicoes> punicoes) {
        this.punicoes = punicoes;
    }

	public Set<Elogios> getElogios() {
        return this.elogios;
    }

	public void setElogios(Set<Elogios> elogios) {
        this.elogios = elogios;
    }

	public Set<Meritos> getMeritos() {
        return this.meritos;
    }

	public void setMeritos(Set<Meritos> meritos) {
        this.meritos = meritos;
    }

	public String getEmailResponsavel() {
        return this.emailResponsavel;
    }

	public void setEmailResponsavel(String emailResponsavel) {
        this.emailResponsavel = emailResponsavel;
    }

	public Set<LancamentosAlunos> getLancamentos() {
        return this.lancamentos;
    }

	public void setLancamentos(Set<LancamentosAlunos> lancamentos) {
        this.lancamentos = lancamentos;
    }
}
